SQLiteを分散データベースに変えるmvSQLite
Introduction
先日Githubに公開されたmvSQLiteですが、
「SQLite互換のスケーラブルなデータベース」とのことで話題になってます。
mvSQLiteの特徴は、SQLiteのストレージレイヤーをFoundationDBに分離しているところです。
これにより、DynamoDBのように際限のないスケーラビリティ、point-in-timeでの読み取り、
そしてRDBの厳密な一貫性を提供します。
作成者曰く、mvSQLiteの目標は
「SQLiteを分散データベースに変えること」
とのことです。
FoundationDBとは
FoundationDBは大量の構造化データを処理するために設計された分散データベースです。
2015年にAppleが買収したことでもニュースになりました。
データをソート済みのKeyValueデータとして保管し、
すべての操作にACIDトランザクションを適用するのが特徴です。
読み込み・書き込みのワークロードに適しており、
特に書き込みを多用するワークロードに
優れたパフォーマンスを発揮するとのことです。
1コアで20,000書き込み/秒のスループット(SSDを使う場合)を発揮するとか、
500コアまでリニアにスケール可能とか、大規模なシステムを想定して設計されています。
ちなみにDBとの接続はAPIの言語バインディングを使用して行います。
こんなのとか。
mvSQLite features
SQLiteと完全互換
mvSQLiteでは、カスタムVFSレイヤもしくはFUSE(選択可能)を使ってSQLiteと統合されます。
SQLiteの下層レイヤーとして動作するため、SQLiteすべての機能が使用可能。
スケーラブルな読み込み・書き込み
FoundationDBのメリット
(スケーラブルな分散トランザクション、同期/非同期レプリケーションなど)を
取り入れつつ、FoundationDBがサポートしていない5秒以上のトランザクションや
大きいサイズのトランザクションが可能になっています。
Drop-in addition
環境変数設定(LD_PRELOAD=libmvsqlite_preload.soもしくはFUSE)することで
既存のSQLiteベースのアプリを動作させることが可能。
ACIDより厳しい制約
mvSQLiteはACIDよりも厳密な保証を提供します。
外部整合性なのでトランザクション処理システムにとって
最も厳密な整合性レベルを採用しています。
また、FoundationDBは同期レプリケーションを行って耐久性を保証します。
必要に応じて、グローバルな結果整合性のある低レイテンシーの
読み取りを実行するオプションも持っており、
さまざまなリージョンへの非同期レプリケーション (DR) を行うことも可能になってます。
ポイントインタイムでの読み取り
mvSQLiteはMVCC (マルチバージョン同時実行制御) をサポートしています。
過去のどの時点でもデータベースのスナップショットを開いて読み取ることができます。
Setup
mvSQLiteを動かしてみましょう。
環境は、EC2でubuntu
(ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-20220609)のAMI
を使用してインスタンスを起動しました。
sshログインして作業開始。
% ssh -i <ssh key> ubuntu@<IPアドレス>
最初に必要なツール郡をインストールしておきましょう。
% sudo apt-get update % sudo apt-get install -y wget % sudo apt-get install -y zip % sudo apt-get install -y gcc
ここにある手順でインストールを行います。
まずはFoundationDBのインストール。
% wget https://github.com/apple/foundationdb/releases/download/7.1.15/foundationdb-clients_7.1.15-1_amd64.deb % sudo dpkg -i foundationdb-clients_7.1.15-1_amd64.deb % wget https://github.com/apple/foundationdb/releases/download/7.1.15/foundationdb-server_7.1.15-1_amd64.deb % sudo dpkg -i foundationdb-server_7.1.15-1_amd64.deb
必要なファイルをダウンロードして実行。
% curl -L -o ./libmvsqlite_preload.so https://github.com/losfair/mvsqlite/releases/download/v0.1.15/libmvsqlite_preload.so % curl -L -o ./mvstore https://github.com/losfair/mvsqlite/releases/download/v0.1.15/mvstore % RUST_LOG=info ./mvstore \ --data-plane 127.0.0.1:7000 \ --admin-api 127.0.0.1:7001 \ --metadata-prefix mvstore-test \ --raw-data-prefix m 2022-08-25T06:12:14.575825Z INFO mvstore: server initialized at mvstore/src/main.rs:173 2022-08-25T06:12:14.582580Z INFO mvstore::server: timekeeper started at mvstore/src/server.rs:651
% ps -ax | grep mvstore 2582 pts/0 Sl+ 0:00 ./mvstore --data-plane 127.0.0.1:7000 --admin-api 127.0.0.1:7001 --metadata-prefix mvstore-test --raw-data-prefix m
起動したみたいなのでOK。
Admin APIをつかってnamespaceを作ってみる。
% curl http://localhost:7001/api/create_namespace -i -d '{"key":"test","metadata":""}' HTTP/1.1 200 OK content-length: 2 date: Thu, 25 Aug 2022 06:13:32 GMT ok
libsqlite3とsqlite3 CLIをビルドします。
% wget https://www.sqlite.org/2022/sqlite-amalgamation-3390200.zip % unzip sqlite-amalgamation-3390200.zip % cd sqlite-amalgamation-3390200 % gcc -O2 -fPIC --shared -o libsqlite3.so ./sqlite3.c -lpthread -ldl -lm % gcc -O2 -o sqlite3 ./shell.c -L. -lsqlite3
Githubにある手順だとここで環境変数を指定してシェルを実行しているのですが、
私の環境だとlibssl.so.1.1がないとかエラーになったので、
↓の手順でopensslをビルドして回避。
もっといい方法があると思いますが、とりあえず動かすだけなのでよしとする。
% wget https://www.openssl.org/source/openssl-1.1.1o.tar.gz % tar -zxvf openssl-1.1.1o.tar.gz % cd openssl-1.1.1o % ./config % make % make test % sudo make install
改めて、環境変数の設定とシェルの起動。
LD_LIBRARY_PATHに設定しているopenssl-1.1.1oは、
↑でビルドしたパスを指定しました。
% export RUST_LOG=info MVSQLITE_DATA_PLANE="http://localhost:7000" % LD_PRELOAD=/path/your/libmvsqlite_preload.so LD_LIBRARY_PATH=.:/path/your/openssl-1.1.1o ./sqlite3 test
これでsqliteのシェルが表示されます。
動かしてみましょう。
まずは適当なテーブルをcreateしてみます。
SQLite version 3.39.2 2022-07-21 15:24:47 Enter ".help" for usage hints. sqlite> create table user (id integer primary key,name text not null); 2022-08-25T06:39:59.696059Z INFO mvsqlite: mvsqlite initialized, sector_size: 8192 at mvsqlite/src/lib.rs:76 2022-08-25T06:39:59.696468Z WARN mvfs::vfs: read_exact_at called without a transaction, offset: 0, len: 100 at mvfs/src/vfs.rs:316 2022-08-25T06:39:59.709848Z INFO mvfs::vfs: transaction committed, version: "000000006d786fc50000", duration: 3.943723ms, num_pages: 2, rea d_version: "00000000000000000000", last_version: "00000000000000000000" at mvfs/src/vfs.rs:527
なんかログ出てます。
トランザクションがコミットされるとその時点でのバージョンが表示されてますね。
適当なデータをinsertしてselectしてみます。
sqlite> insert into user values (1,'syuta'); 2022-08-25T06:40:56.246544Z INFO mvfs::vfs: identity write ignored, page: 0 at mvfs/src/vfs.rs:374 2022-08-25T06:40:56.253111Z INFO mvfs::vfs: transaction committed, version: "0000000070d7391b0000", duration: 3.420705ms, num_pages: 1, read_version: "000000006d786fc50000", last_version: "000000006d786fc50000" at mvfs/src/vfs.rs:527 sqlite> insert into user values (2,'taro'); 2022-08-25T06:41:22.843304Z INFO mvfs::vfs: identity write ignored, page: 0 at mvfs/src/vfs.rs:374 2022-08-25T06:41:22.889989Z INFO mvfs::vfs: transaction committed, version: "00000000726daafa0000", duration: 3.63283ms, num_pages: 1, read_version: "0000000070d7391b0000", last_version: "0000000070d7391b0000" at mvfs/src/vfs.rs:527 sqlite> select * from user; 1|syuta 2|taro
普通にSQLite使ってるのとまったく同じです。
Time travel(checkout past snapshots)を試してみる
ここでシェルを一旦終了し、
「1レコードコミットした時点でのバージョン」を指定して再度つなげてみます。
バージョンIDは「0000000070d7391b0000」なので、
それを指定して(DB名@<トランザクションID)シェルを起動します。
LD_PRELOAD=/path/your/libmvsqlite_preload.so LD_LIBRARY_PATH=.:/path/your/openssl-1.1.1o ./sqlite3 test@0000000070d7391b0000 SQLite version 3.39.2 2022-07-21 15:24:47 Enter ".help" for usage hints. sqlite> select * from user; 2022-08-25T06:51:47.898922Z INFO mvsqlite: mvsqlite initialized, sector_size: 8192 at mvsqlite/src/lib.rs:76 2022-08-25T06:51:47.899325Z WARN mvfs::vfs: read_exact_at called without a transaction, offset: 0, len: 100 at mvfs/src/vfs.rs:316 1|syuta
データを確認すると、たしかにその時点でのデータになってます。
Summary
というわけで、mvSQLiteを動かしてみました。
現在はβ版でバグもありますが、
SQLite完全互換でスケーラブルな分散DBということで
今後が楽しみです。